
#include "serial.h"
#include <string.h>

SBIT(SERIAL_RTS,0xA0,4);//sbit P2^4;
#define SERIAL_RTS_ENABLE SERIAL_RTS=0;isallowreceive=true
#define SERIAL_RTS_DISABLE SERIAL_RTS=1;isallowreceive=false

//buf_send params
#define SERSNDBUFLEN 20
static volatile uchar bsnd[SERSNDBUFLEN];
static volatile uchar bsnd_sendpos;
static volatile uchar bsnd_loadpos;
static volatile uchar bsnd_remain;
//buf_receive params
#define SERRECBUFLEN 24
static volatile uchar brec[SERRECBUFLEN];
static volatile uchar brec_recpos;
static volatile uchar brec_readpos;
static volatile uchar brec_remain;
//buf_receive flow control
static volatile bool isallowreceive=false;

//all params
static volatile bool issending=false;
static volatile bool isworking=false;

void SERIAL_INIT()
{
	//init T1, which used for baud rate generation
	TMOD(SERIALTIMERID,2);
	PCON|=0x80;//SMOD in PCON set
	TH(SERIALTIMERID)=0xf3;//9600
	TL(SERIALTIMERID)=0xf3;
	ET(SERIALTIMERID)=0;
	SCON=0x40;//serial mode 1

//	SERIAL_BAUD_X12();//9600x12

	SERIAL_RTS_ENABLE;
}

void SERIAL_BAUD_X12(bool enable) {
	Tx12(SERIALTIMERID,enable);
}

void SERIAL_ENABLE()
{
	bsnd_sendpos=0;
	bsnd_loadpos=0;
	bsnd_remain=0;
	brec_recpos=0;
	brec_readpos=0;
	brec_remain=0;

	issending=false;

	TR(SERIALTIMERID)=1;
	ES=1;
	REN=1;

	isworking=true;
}

void SERIAL_DISABLE()
{
	ES=0;
	REN=0;
	TR(SERIALTIMERID)=0;
	isworking=false;
}

//guaranteed send function for sending String
void SERIAL_SEND_S(uchar *pointer) {
	SERIAL_SEND_CA(pointer,strlen(pointer));
}
//guaranteed send function for single Char
void SERIAL_SEND_C(uchar ch) {
	SERIAL_SEND_CA(&ch,1);
}

//guaranteed send function for Char Array
void SERIAL_SEND_CA(uchar *pointer, uchar len) {
	while( !SERIAL_UNSAFE_SEND(pointer,len) ){;}
}
//nonblock send data, max length=SERSNDBUFLEN-bsnd_remain
bool SERIAL_UNSAFE_SEND(uchar *pointer, uchar len)
{
	uchar i;
	if(!isworking) return false;
	if(len>SERSNDBUFLEN-bsnd_remain) return false;//this ensures there's no overflow for send buffer

	for(i=0;i<len;i++)
	{
		bsnd[bsnd_loadpos]=pointer[i];
		POSPLUS(bsnd_loadpos,SERSNDBUFLEN);
	}

	ES=0;
	bsnd_remain+=len;
	ES=1;
	
	if(!issending)
	{
		ES=0;
		SBUF=bsnd[bsnd_sendpos];
		POSPLUS(bsnd_sendpos,SERSNDBUFLEN);
		issending=true;
		bsnd_remain--;
		ES=1;
	}

	return true;
}

void SERIAL_FLUSH_GET() {
	if(!isworking) return;

	REN=0;
	brec_recpos=0;
	brec_readpos=0;
	brec_remain=0;
	REN=1;
}

bool SERIAL_TRY_GET_C(uchar tryCount, uchar *pointer) {
	return SERIAL_TRY_GET_CA(tryCount, pointer, 1);
}
bool SERIAL_TRY_GET_CA(uchar tryCount, uchar *pointer, uchar len) {
	bool retval;
	if(tryCount==0) return false;
	do {
		retval=SERIAL_UNSAFE_GET(pointer,len);
		tryCount--;
	}
	while( !retval && tryCount>0 );
	return retval;
}

bool SERIAL_UNSAFE_GET(uchar *pointer, uchar len) {
	uchar remain,i;
	if(!isworking) return false;

	if(len==0) return true;
	remain=brec_remain;
	if(len>remain) return false;


	for(i=0;i<len;i++) {
		pointer[i]=brec[brec_readpos];
		POSPLUS(brec_readpos,SERRECBUFLEN);
	}
	ES=0;
	brec_remain-=len;
	SERIAL_UPDATE_FLOWCTRL();
	ES=1;
	return true;
}

//global param used in this func
//for sending:
//		issending
//		bsnd[]
//		bsnd_sendpos
//		bsnd_remain
//	note: use ES=0 to protect above global vars used in other place
//for receive:
//		brec[]
//		brec_recpos
//		bred_remain
//	note: use REN=0 to protect above global vars used in other place
void serial(void) interrupt SERIALINTERRUPTID
{
	ES=0;
	if(TI==1)
	{
		//sent OK
		TI=0;

		if(issending)
		{
			if(bsnd_remain==0)
			{
				//send all OK, restore state
				issending=false;
			}
			else
			{
				SBUF=bsnd[bsnd_sendpos];
				POSPLUS(bsnd_sendpos,SERSNDBUFLEN);
				bsnd_remain--;
			}
		}
		else
		{
			//error, shouldn't run here, just restore state
		}

	}
	if(RI==1)
	{
		//received
		RI=0;
		brec[brec_recpos]=SBUF;
		POSPLUS(brec_recpos,SERRECBUFLEN);
		brec_remain++;

		//flow control
		if(brec_remain>SERRECBUF_FLOWCTRL_UP) {
			//reach up threshold
			SERIAL_RTS_DISABLE;
		}
	}
	ES=1;
}

bool SERIAL_ISSENDING() {
	return issending;
}

void SERIAL_UPDATE_FLOWCTRL() {
	uchar remain;
	if( !isallowreceive ) {
		remain=brec_remain;
		if(remain<SERRECBUF_FLOWCTRL_LOW) {
			SERIAL_RTS_ENABLE;
		}
	}
}


